﻿using Ducu.BLL.Entity;
using Ducu.BLL.NHMap;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Context;
using NHibernate.Dialect;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using NHibernate.Linq;
using NHibernate.Caches.SysCache;

namespace Ducu.SRV.ProdService
{
    public class RepoFactory
    {
        #region Create Session Factory

        private static ISessionFactory sessionFactory;

        static RepoFactory()
        {
            //var log = log4net.LogManager.GetLogger("ServiceInfo");
            //log.Info("begin to build a session factory");

            string connStr =
                ConfigurationManager.ConnectionStrings["dev"].ConnectionString;
            sessionFactory = Fluently.Configure()
                .Database(
                    MySQLConfiguration.Standard.
                    ConnectionString(connStr).Dialect<MySQL5Dialect>()
#if DEBUG
.ShowSql().FormatSql()
#endif
)
                .Mappings(ConfigurationProvider.Action)
                .Cache(x => x.UseSecondLevelCache().ProviderClass<SysCacheProvider>())
                .ExposeConfiguration(c => c.SetProperty(
                        NHibernate.Cfg.Environment.CurrentSessionContextClass, "web"))
                .BuildSessionFactory();

            //log.Info("has completed to build the session factory");

        }

        #endregion

        #region Init Session

        private ISession _session;

        public RepoFactory()
        {
            initSession();
        }

        private void initSession()
        {
            if (!CurrentSessionContext.HasBind(sessionFactory))
            {
                _session = sessionFactory.OpenSession();
                CurrentSessionContext.Bind(_session);
            }
            else
            {
                _session = sessionFactory.GetCurrentSession();
            }

            //TODO: not sure the meaning of IsActive
            //seems _session.Transaction can't be null always
            if (!_session.Transaction.IsActive)
            {
                _session.BeginTransaction();
            }
        }

        #endregion

        #region End Session

        public static void EndSession()
        {
            if (CurrentSessionContext.HasBind(sessionFactory))
            {
                //NOTE: must be session from context
                ISession sessionFromContext = sessionFactory.GetCurrentSession();
                using (sessionFromContext)
                {
                    using (sessionFromContext.Transaction)
                    {
                        try
                        {
                            sessionFromContext.Transaction.Commit();
                        }
                        catch (Exception)
                        {
                            sessionFromContext.Transaction.Rollback();
                            throw;
                        }
                    }

                    //needn't since Commit() will do Flush() by default.
                    //session.Flush();
                }

                CurrentSessionContext.Unbind(sessionFactory);
            }
        }

        #endregion

        #region Common Methods

        public T Load<T>(object id) where T : BaseEntity
        {
            return _session.Load<T>(id);
        }

        public T Get<T>(object id) where T : BaseEntity
        {
            return _session.Get<T>(id);
        }

        public int Save<T>(T entity) where T : BaseEntity
        {
            return Convert.ToInt32(_session.Save(entity));
        }

        public void SaveOrUpdate<T>(T entity) where T : BaseEntity
        {
            _session.SaveOrUpdate(entity);
        }

        public void Delete<T>(T entity) where T : BaseEntity
        {
            _session.Delete(entity);
        }

        public IQueryable<T> Query<T>() where T : BaseEntity
        {
            return _session.Query<T>();
        }

        #endregion
    }
}
